package com.base.syslib.util;

import android.text.TextUtils;
import android.util.Log;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import org.json.JSONArray;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @JsonInclude(JsonInclude.Include.NON_NULL) //为null不参与序列化
 */
public class JsonParse {
    private static JsonParse mInstance = null;


    private ObjectMapper objectMapper = null;

    protected JsonParse() {

        objectMapper = new ObjectMapper();
        SimpleDateFormat fmt = new SimpleDateFormat(
                "yyyy-MM-dd HH:mm:ss");
        objectMapper.setDateFormat(fmt);

        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);//允许转义字符
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);//允许单引号
        objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
//        //字段和值加引号
//        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES,true);
//        objectMapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES,true);
//        数字也加引号
//        objectMapper.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS,true);
//        objectMapper.configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS,true);
//        mapkey排序
//        objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, false);
//        数值为null时不序列化为json
//        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//         过滤map中的null值
//        objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, true);
        objectMapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, true);
//            objectMapper.getDeserializationConfig().with(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//            objectMapper.getFactory().createGenerator(System.out, JsonEncoding.UTF8);
    }

    public static JsonParse getInstance() {
        if (mInstance == null) {
            synchronized (JsonParse.class) {
                if (mInstance == null) {

                    mInstance = new JsonParse();
                }
            }
        }
        return mInstance;
    }

    public ObjectMapper JsonMapper() {

        return objectMapper;
    }

    /**
     * 获得泛型类型
     *
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getGenericObject(String json, Class<T> clazz) {
        Type type = clazz.getGenericSuperclass();

        if (type != null && type instanceof ParameterizedType) {
            ParameterizedType poClass = (ParameterizedType) type;
            Class<T> genericClazz = (Class) poClass.getActualTypeArguments()[0];
            return getObjectFromJson(json, genericClazz);
        }
        return null;
    }

    public JsonFactory getJsonFactory() {
        return objectMapper.getFactory();
    }

    /**
     * @param jsonString
     * @param valueType
     * @param <T>
     * @return
     */
    public <T> T getObjectFromJson(String jsonString, Class<T> valueType) {
        try {
            return objectMapper.readValue(jsonString, valueType);
        } catch (Exception e) {
            e.printStackTrace();
            //            Log.e ("tag",e.toString ());
            return null;
        }
    }

    public <T> T getObjectFromJson(File jsonFile, Class<T> valueType) {
        try {
            return objectMapper.readValue(jsonFile, valueType);
        } catch (IOException e) {
            Log.e("json", "read json failed! type = " + valueType.toString());
            return null;
        }
    }

    public String getJsonFromObject(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getValueArray(Object obj) {
        JsonNode rootNode = objectMapper.convertValue(obj, JsonNode.class);
        Iterator<JsonNode> iterator = rootNode.elements();
        JSONArray jsonArray = new JSONArray();
        while (iterator.hasNext()) {
            JsonNode node = iterator.next();
            jsonArray.put(node.asText());
        }
        return jsonArray.toString();
    }


    public <T> List<T> parseList(String json, Class<T> clazz) {
        if (TextUtils.isEmpty(json)) {
            return new ArrayList<T>();
        }
        try {

//            Map<String,User> result = objectMapper.readValue(json, new TypeReference<Map<String,User>>() { });
            JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, clazz);

            return objectMapper.readValue(json, javaType);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return new ArrayList<T>();
    }
    public <K,V> HashMap<K,V> getHashMap(String json, Class<? extends Map> mapClazz, Class<K> clazzK, Class<V> clazzV) {
        if (TextUtils.isEmpty(json)) {
            return new HashMap<K,V>();
        }
        try {
//            Map<String,User> result = objectMapper.readValue(json, new TypeReference<Map<String,User>>() { });
            JavaType javaType = objectMapper.getTypeFactory().constructMapType(mapClazz,clazzK,clazzV);
            return objectMapper.readValue(json, javaType);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return new HashMap<K,V>();
    }

    public <T> String listToJsonArray(List<T> list) {
        if (!CollectionUtil.isValid(list)) {
            return "";
        }
        try {
            return objectMapper.writeValueAsString(list);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return "";
    }

    public static class NullStringSerializer extends JsonSerializer<String> {
        @Override
        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            if (value == null) {
//                jgen.writeNull();
                jgen.writeString("");
            } else {
                jgen.writeString(value);
            }

        }
    }

    public static class NullSerializer extends StdSerializer<Object> {


        protected NullSerializer(Class<Object> t) {
            super(t);
        }

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonGenerationException {
            jsonGenerator.writeNull();
        }

        public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
            return this.createSchemaNode("null");
        }

        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
            visitor.expectNullFormat(typeHint);
        }
    }

    public static class NullStringDeSerializer extends JsonDeserializer<String> {

        @Override
        public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            if (TextUtils.isEmpty(jsonParser.getText())) {
                return "";
            }
            if ("null".equals(jsonParser.getText().toLowerCase())) {
                return "";
            }

            return jsonParser.getText();
        }
    }

    public void serialize(Object object) {
        try {
            JsonGenerator jsonGenerator =
                    objectMapper.getFactory().createGenerator(System.out, JsonEncoding.UTF8);
            if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
                jsonGenerator.useDefaultPrettyPrinter();
            }
            this.objectMapper.writeValue(jsonGenerator, object);
        } catch (JsonProcessingException ex) {
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class MyNullArrayJsonSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (value == null) {
            jgen.writeStartArray();
            jgen.writeEndArray();
        } else {
            jgen.writeObject(value);
        }
    }
}

class NullStringJsonSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (value == null) {
            jgen.writeString("");
        } else {
            jgen.writeObject(value);
        }
    }
}

class NullObjectJsonSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (value == null) {
//            jgen.writeStartObject();
//            jgen.writeEndObject();
            jgen.writeNull();

        } else {
            jgen.writeObject(value);
        }
    }
}

class NullNumberJsonSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (value == null) {
            jgen.writeNumber(0);
        } else {
            jgen.writeObject(value);
        }
    }
}

class NullBooleanJsonSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (value == null) {
            jgen.writeBoolean(false);
        } else {
            jgen.writeObject(value);
        }
    }
}

class MyBeanSerializerModifier extends BeanSerializerModifier {

    private JsonSerializer<Object> _nullArrayJsonSerializer = new MyNullArrayJsonSerializer();
    private JsonSerializer<Object> _nullStringJsonSerializer = new NullStringJsonSerializer();
    private JsonSerializer<Object> _nullObjectSerializer = new NullObjectJsonSerializer();
    private JsonSerializer<Object> _nullNumberSerializer = new NullNumberJsonSerializer();
    private JsonSerializer<Object> _nullBooleanSerializer = new NullBooleanJsonSerializer();

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
                                                     List<BeanPropertyWriter> beanProperties) {
        // 循环所有的beanPropertyWriter
        for (int i = 0; i < beanProperties.size(); i++) {
            BeanPropertyWriter writer = beanProperties.get(i);
            // 判断字段的类型，如果是array，list，set则注册nullSerializer
            if (isArrayType(writer)) {
                //给writer注册一个自己的nullSerializer
                writer.assignNullSerializer(this.defaultNullArrayJsonSerializer());
            } else if (isPrimitive(writer) || isNumber(writer)) {
                writer.assignNullSerializer(this.defaultNullNumberObjectJsonSerializer());
            } else if (isBoolean(writer)){
                writer.assignNullSerializer(this.defaultNullBooleanObjectJsonSerializer());
            } else if (isStringType(writer)) {
                writer.assignNullSerializer(this.defaultNullStringJsonSerializer());
            } else {
                writer.assignNullSerializer(this.defaultNullObjectJsonSerializer());
            }
        }
        return beanProperties;
    }

    // 判断是什么类型
    protected boolean isArrayType(BeanPropertyWriter writer) {
        Class<?> clazz = writer.getPropertyType();
//        Map.class.isAssignableFrom(clazz)||//不能判断，map序列化为{}
        return Collection.class.isAssignableFrom(clazz)||clazz.isArray();
    }

    protected boolean isStringType(BeanPropertyWriter writer) {
//        return writer.getPropertyType().equals(String.class) || writer.getPropertyType().isEnum()||writer.getPropertyType() == Date.class||writer.getPropertyType() == Timestamp.class;
        return writer.getPropertyType().equals(String.class) || writer.getPropertyType().isEnum();
    }

    //基本数据类型
    protected boolean isPrimitive(BeanPropertyWriter writer) {
        return writer.getPropertyType().isPrimitive();
    }
    protected boolean isBoolean(BeanPropertyWriter writer){
        return writer.getPropertyType() ==Boolean.class;
    }
    //基本数据类型
    protected boolean isNumber(BeanPropertyWriter writer) {
        return Number.class.isAssignableFrom(writer.getPropertyType());
    }


    protected JsonSerializer<Object> defaultNullArrayJsonSerializer() {
        return _nullArrayJsonSerializer;
    }

    protected JsonSerializer<Object> defaultNullStringJsonSerializer() {
        return _nullStringJsonSerializer;
    }

    protected JsonSerializer<Object> defaultNullObjectJsonSerializer() {
        return _nullObjectSerializer;
    }

    protected JsonSerializer<Object> defaultNullNumberObjectJsonSerializer() {
        return _nullNumberSerializer;
    }
    protected JsonSerializer<Object> defaultNullBooleanObjectJsonSerializer() {
        return _nullBooleanSerializer;
    }
}